home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / getchar.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  34KB  |  1,476 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * getchar.c
  11.  *
  12.  * functions related with getting a character from the user/mapping/redo/...
  13.  *
  14.  * manipulations with redo buffer and stuff buffer
  15.  * mappings and abbreviations
  16.  */
  17.  
  18. #include "vim.h"
  19. #include "globals.h"
  20. #include "proto.h"
  21. #include "param.h"
  22.  
  23. /*
  24.  * structure used to store one block of the stuff/redo/macro buffers
  25.  */
  26. struct bufblock
  27. {
  28.         struct bufblock *b_next;        /* pointer to next bufblock */
  29.         char_u            b_str[1];        /* contents (actually longer) */
  30. };
  31.  
  32. #define MINIMAL_SIZE 20                 /* minimal size for b_str */
  33.  
  34. /*
  35.  * header used for the stuff buffer and the redo buffer
  36.  */
  37. struct buffheader
  38. {
  39.         struct bufblock bh_first;        /* first (dummy) block of list */
  40.         struct bufblock *bh_curr;        /* bufblock for appending */
  41.         int             bh_index;        /* index for reading */
  42.         int             bh_space;        /* space in bh_curr for appending */
  43. };
  44.  
  45. static struct buffheader stuffbuff = {{NULL, {NUL}}, NULL, 0, 0};
  46. static struct buffheader redobuff = {{NULL, {NUL}}, NULL, 0, 0};
  47. static struct buffheader recordbuff = {{NULL, {NUL}}, NULL, 0, 0};
  48.  
  49.     /*
  50.      * when block_redo is TRUE redo buffer will not be changed
  51.      * used by edit() to repeat insertions and 'V' command for redoing
  52.      */
  53. static int        block_redo = FALSE;
  54.  
  55. /*
  56.  * structure used for mapping
  57.  */
  58. struct mapblock
  59. {
  60.     struct mapblock *m_next;        /* next mapblock */
  61.     char_u            *m_keys;        /* mapped from */
  62.     int                 m_keylen;        /* strlen(m_keys) */
  63.     char_u            *m_str;         /* mapped to */
  64.     int              m_mode;        /* valid mode */
  65.     int                 m_noremap;        /* if non-zero no re-mapping for m_str */
  66. };
  67.  
  68. static struct mapblock maplist = {NULL, NULL, 0, NULL, 0, 0};
  69.                                     /* first dummy entry in maplist */
  70.  
  71. /*
  72.  * variables used by vgetorpeek() and flush_buffers()
  73.  *
  74.  * typestr contains all characters that are not consumed yet.
  75.  * The part in front may contain the result of mappings, abbreviations and
  76.  * @a commands. The lenght of this part is typemaplen.
  77.  * After it are characters that come from the terminal.
  78.  * no_abbr_cnt is the number of characters in typestr that should not be
  79.  * considered for abbreviations.
  80.  * Some parts of typestr may not be mapped. These parts are remembered in
  81.  * the noremaplist. 
  82.  */
  83. #define MAXMAPLEN 50                /* maximum length of key sequence to be mapped */
  84.                                     /* must be able to hold an Amiga resize report */
  85. static char_u    *typestr = NULL;    /* NUL-terminated buffer for typeahead characters */
  86. static char_u    typebuf[MAXMAPLEN + 3]; /* initial typestr */
  87.  
  88. static int        typemaplen = 0;        /* number of mapped characters in typestr */
  89. static int        no_abbr_cnt = 0;    /* number of chars without abbrev. in typestr */
  90.  
  91. /* 
  92.  * parts int typestr that should not be mapped are remembered with a list
  93.  * of noremap structs. Noremaplist is the first.
  94.  */
  95. struct noremap
  96. {
  97.     int                nr_off;            /* offset to not remappable chars */
  98.     int                nr_len;            /* number of not remappable chars */
  99.     struct noremap    *nr_next;        /* next entry in the list */
  100. };
  101.  
  102. static struct noremap noremaplist = {0, 0, NULL};
  103.  
  104. static void        free_buff __ARGS((struct buffheader *));
  105. static char_u    *get_bufcont __ARGS((struct buffheader *, int));
  106. static void        add_buff __ARGS((struct buffheader *, char_u *));
  107. static void        add_num_buff __ARGS((struct buffheader *, long));
  108. static void        add_char_buff __ARGS((struct buffheader *, int));
  109. static int        read_stuff __ARGS((int));
  110. static void        start_stuff __ARGS((void));
  111. static int        read_redo __ARGS((int));
  112. static void        init_typestr __ARGS((void));
  113. static void        gotchars __ARGS((char_u *, int));
  114. static int        vgetorpeek __ARGS((int));
  115. static void        showmap __ARGS((struct mapblock *));
  116.  
  117. /*
  118.  * free and clear a buffer
  119.  */
  120.     static void
  121. free_buff(buf)
  122.     struct buffheader *buf;
  123. {
  124.         register struct bufblock *p, *np;
  125.  
  126.         for (p = buf->bh_first.b_next; p != NULL; p = np)
  127.         {
  128.                 np = p->b_next;
  129.                 free(p);
  130.         }
  131.         buf->bh_first.b_next = NULL;
  132. }
  133.  
  134. /*
  135.  * return the contents of a buffer as a single string
  136.  */
  137.     static char_u *
  138. get_bufcont(buffer, dozero)
  139.     struct buffheader    *buffer;
  140.     int                    dozero;        /* count == zero is not an error */
  141. {
  142.         long_u            count = 0;
  143.         char_u            *p = NULL;
  144.         struct bufblock    *bp;
  145.  
  146. /* compute the total length of the string */
  147.         for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
  148.                 count += STRLEN(bp->b_str);
  149.  
  150.         if ((count || dozero) && (p = lalloc(count + 1, TRUE)) != NULL)
  151.         {
  152.                 *p = NUL;
  153.                 for (bp = buffer->bh_first.b_next; bp != NULL; bp = bp->b_next)
  154.                         strcat((char *)p, (char *)bp->b_str);
  155.         }
  156.         return (p);
  157. }
  158.  
  159. /*
  160.  * return the contents of the record buffer as a single string
  161.  *    and clear the record buffer
  162.  */
  163.     char_u *
  164. get_recorded()
  165. {
  166.     char_u *p;
  167.  
  168.     p = get_bufcont(&recordbuff, TRUE);
  169.     free_buff(&recordbuff);
  170.     return (p);
  171. }
  172.  
  173. /*
  174.  * return the contents of the redo buffer as a single string
  175.  */
  176.     char_u *
  177. get_inserted()
  178. {
  179.         return(get_bufcont(&redobuff, FALSE));
  180. }
  181.  
  182. /*
  183.  * add string "s" after the current block of buffer "buf"
  184.  */
  185.     static void
  186. add_buff(buf, s)
  187.     register struct buffheader    *buf;
  188.     char_u                        *s;
  189. {
  190.     struct bufblock *p;
  191.     long_u             n;
  192.     long_u             len;
  193.  
  194.     if ((n = STRLEN(s)) == 0)                /* don't add empty strings */
  195.         return;
  196.  
  197.     if (buf->bh_first.b_next == NULL)        /* first add to list */
  198.     {
  199.         buf->bh_space = 0;
  200.         buf->bh_curr = &(buf->bh_first);
  201.     }
  202.     else if (buf->bh_curr == NULL)            /* buffer has already been read */
  203.     {
  204.         EMSG("Add to read buffer");
  205.         return;
  206.     }
  207.     else if (buf->bh_index != 0)
  208.         STRCPY(buf->bh_first.b_next->b_str, buf->bh_first.b_next->b_str + buf->bh_index);
  209.     buf->bh_index = 0;
  210.  
  211.     if (buf->bh_space >= n)
  212.     {
  213.         strcat((char *)buf->bh_curr->b_str, (char *)s);
  214.         buf->bh_space -= n;
  215.     }
  216.     else
  217.     {
  218.         if (n < MINIMAL_SIZE)
  219.             len = MINIMAL_SIZE;
  220.         else
  221.             len = n;
  222.         p = (struct bufblock *)lalloc((long_u)(sizeof(struct bufblock) + len), TRUE);
  223.         if (p == NULL)
  224.             return; /* no space, just forget it */
  225.         buf->bh_space = len - n;
  226.         STRCPY(p->b_str, s);
  227.  
  228.         p->b_next = buf->bh_curr->b_next;
  229.         buf->bh_curr->b_next = p;
  230.         buf->bh_curr = p;
  231.     }
  232.     return;
  233. }
  234.  
  235.     static void
  236. add_num_buff(buf, n)
  237.     struct buffheader *buf;
  238.     long               n;
  239. {
  240.         char_u    number[32];
  241.  
  242.         sprintf((char *)number, "%ld", n);
  243.         add_buff(buf, number);
  244. }
  245.  
  246.     static void
  247. add_char_buff(buf, c)
  248.     struct buffheader *buf;
  249.     int               c;
  250. {
  251.         char_u    temp[2];
  252.  
  253.         temp[0] = c;
  254.         temp[1] = NUL;
  255.         add_buff(buf, temp);
  256. }
  257.  
  258. /*
  259.  * get one character from the stuff buffer
  260.  * If advance == TRUE go to the next char.
  261.  */
  262.     static int
  263. read_stuff(advance)
  264.     int            advance;
  265. {
  266.     register char_u c;
  267.     register struct bufblock *curr;
  268.  
  269.  
  270.     if (stuffbuff.bh_first.b_next == NULL)    /* buffer is empty */
  271.         return NUL;
  272.  
  273.     curr = stuffbuff.bh_first.b_next;
  274.     c = curr->b_str[stuffbuff.bh_index];
  275.  
  276.     if (advance)
  277.     {
  278.         if (curr->b_str[++stuffbuff.bh_index] == NUL)
  279.         {
  280.             stuffbuff.bh_first.b_next = curr->b_next;
  281.             free(curr);
  282.             stuffbuff.bh_index = 0;
  283.         }
  284.     }
  285.     return c;
  286. }
  287.  
  288. /*
  289.  * prepare stuff buffer for reading (if it contains something)
  290.  */
  291.     static void
  292. start_stuff()
  293. {
  294.     if (stuffbuff.bh_first.b_next != NULL)
  295.     {
  296.         stuffbuff.bh_curr = &(stuffbuff.bh_first);
  297.         stuffbuff.bh_space = 0;
  298.     }
  299. }
  300.  
  301. /*
  302.  * check if the stuff buffer is empty
  303.  */
  304.     int
  305. stuff_empty()
  306. {
  307.     return (stuffbuff.bh_first.b_next == NULL);
  308. }
  309.  
  310. /*
  311.  * Remove the contents of the stuff buffer and the mapped characters in the
  312.  * typeahead buffer (used in case of an error). If 'typeahead' is true,
  313.  * flush all typeahead characters (used when interrupted by a CTRL-C).
  314.  */
  315.     void
  316. flush_buffers(typeahead)
  317.     int typeahead;
  318. {
  319.     struct noremap *p;
  320.  
  321.     init_typestr();
  322.  
  323.     start_stuff();
  324.     while (read_stuff(TRUE) != NUL)
  325.         ;
  326.  
  327.     if (typeahead)            /* remove all typeahead */
  328.     {
  329.             /*
  330.              * We have to get all characters, because we may delete the first
  331.              * part of an escape sequence.
  332.              * In an xterm we get one char at a time and we have to get them all.
  333.              */
  334.         while (inchar(typestr, MAXMAPLEN, 10))    
  335.             ;
  336.         *typestr = NUL;
  337.     }
  338.     else                    /* remove mapped characters only */
  339.         STRCPY(typestr, typestr + typemaplen);
  340.     typemaplen = 0;
  341.     no_abbr_cnt = 0;
  342.     noremaplist.nr_len = 0;
  343.     noremaplist.nr_off = 0;
  344.     while (noremaplist.nr_next)
  345.     {
  346.         p = noremaplist.nr_next->nr_next;
  347.         free(noremaplist.nr_next);
  348.         noremaplist.nr_next = p;
  349.     }
  350. }
  351.  
  352.     void
  353. ResetRedobuff()
  354. {
  355.     if (!block_redo)
  356.         free_buff(&redobuff);
  357. }
  358.  
  359.     void
  360. AppendToRedobuff(s)
  361.     char_u           *s;
  362. {
  363.     if (!block_redo)
  364.         add_buff(&redobuff, s);
  365. }
  366.  
  367.     void
  368. AppendCharToRedobuff(c)
  369.     int               c;
  370. {
  371.     if (!block_redo)
  372.         add_char_buff(&redobuff, c);
  373. }
  374.  
  375.     void
  376. AppendNumberToRedobuff(n)
  377.     long             n;
  378. {
  379.     if (!block_redo)
  380.         add_num_buff(&redobuff, n);
  381. }
  382.  
  383.     void
  384. stuffReadbuff(s)
  385.     char_u           *s;
  386. {
  387.     add_buff(&stuffbuff, s);
  388. }
  389.  
  390.     void
  391. stuffcharReadbuff(c)
  392.     int               c;
  393. {
  394.     add_char_buff(&stuffbuff, c);
  395. }
  396.  
  397.     void
  398. stuffnumReadbuff(n)
  399.     long    n;
  400. {
  401.     add_num_buff(&stuffbuff, n);
  402. }
  403.  
  404. /*
  405.  * Read a character from the redo buffer.
  406.  * The redo buffer is left as it is.
  407.  * if init is TRUE, prepare for redo, return FAIL if nothing to redo, OK otherwise
  408.  */
  409.     static int
  410. read_redo(init)
  411.     int            init;
  412. {
  413.     static struct bufblock    *bp;
  414.     static char_u            *p;
  415.     int                        c;
  416.  
  417.     if (init)
  418.     {
  419.         if ((bp = redobuff.bh_first.b_next) == NULL)
  420.             return FAIL;
  421.         p = bp->b_str;
  422.         return OK;
  423.     }
  424.     if ((c = *p) != NUL)
  425.     {
  426.         if (*++p == NUL && bp->b_next != NULL)
  427.         {
  428.             bp = bp->b_next;
  429.             p = bp->b_str;
  430.         }
  431.     }
  432.     return c;
  433. }
  434.  
  435. /*
  436.  * copy the rest of the redo buffer into the stuff buffer (could be done faster)
  437.  */
  438.     void
  439. copy_redo()
  440. {
  441.     register int c;
  442.  
  443.     while ((c = read_redo(FALSE)) != NUL)
  444.         stuffcharReadbuff(c);
  445. }
  446.  
  447. extern int redo_Visual_busy;        /* this is in normal.c */
  448.  
  449. /*
  450.  * Stuff the redo buffer into the stuffbuff.
  451.  * Insert the redo count into the command.
  452.  * return FAIL for failure, OK otherwise
  453.  */
  454.     int
  455. start_redo(count)
  456.     long count;
  457. {
  458.     register int c;
  459.  
  460.     if (read_redo(TRUE) == FAIL)    /* init the pointers; return if nothing to redo */
  461.         return FAIL;
  462.  
  463.     c = read_redo(FALSE);
  464.  
  465. /* copy the buffer name, if present */
  466.     if (c == '"')
  467.     {
  468.         add_buff(&stuffbuff, (char_u *)"\"");
  469.         c = read_redo(FALSE);
  470.  
  471. /* if a numbered buffer is used, increment the number */
  472.         if (c >= '1' && c < '9')
  473.             ++c;
  474.         add_char_buff(&stuffbuff, c);
  475.         c = read_redo(FALSE);
  476.     }
  477.  
  478.     if (c == 'v')    /* redo Visual */
  479.     {
  480.         VIsual = curwin->w_cursor;
  481.         redo_Visual_busy = TRUE;
  482.         c = read_redo(FALSE);
  483.     }
  484.  
  485. /* try to enter the count (in place of a previous count) */
  486.     if (count)
  487.     {
  488.         while (isdigit(c))        /* skip "old" count */
  489.             c = read_redo(FALSE);
  490.         add_num_buff(&stuffbuff, count);
  491.     }
  492.  
  493. /* copy from the redo buffer into the stuff buffer */
  494.     add_char_buff(&stuffbuff, c);
  495.     copy_redo();
  496.     return OK;
  497. }
  498.  
  499. /*
  500.  * Repeat the last insert (R, o, O, a, A, i or I command) by stuffing
  501.  * the redo buffer into the stuffbuff.
  502.  * return FAIL for failure, OK otherwise
  503.  */
  504.     int
  505. start_redo_ins()
  506. {
  507.     register int c;
  508.  
  509.     if (read_redo(TRUE) == FAIL)
  510.         return FAIL;
  511.     start_stuff();
  512.  
  513. /* skip the count and the command character */
  514.     while ((c = read_redo(FALSE)) != NUL)
  515.     {
  516.         c = TO_UPPER(c);
  517.         if (strchr("AIRO", c) != NULL)
  518.         {
  519.             if (c == 'O')
  520.                 stuffReadbuff(NL_STR);
  521.             break;
  522.         }
  523.     }
  524.  
  525. /* copy the typed text from the redo buffer into the stuff buffer */
  526.     copy_redo();
  527.     block_redo = TRUE;
  528.     return OK;
  529. }
  530.  
  531.     void
  532. set_redo_ins()
  533. {
  534.     block_redo = TRUE;
  535. }
  536.  
  537.     void
  538. stop_redo_ins()
  539. {
  540.     block_redo = FALSE;
  541. }
  542.  
  543. /*
  544.  * Initialize typestr to point to typebuf.
  545.  * Alloc() cannot be used here: In out-of-memory situations it would
  546.  * be impossible to type anything.
  547.  */
  548.     static void
  549. init_typestr()
  550. {
  551.     if (typestr == NULL)
  552.     {
  553.         typestr = typebuf;
  554.         typebuf[0] = NUL;
  555.     }
  556. }
  557.  
  558. /*
  559.  * insert a string in front of the typeahead buffer (for '@' command and vgetorpeek)
  560.  * return FAIL for failure, OK otherwise
  561.  */
  562.     int
  563. ins_typestr(str, noremap)
  564.     char_u    *str;
  565.     int        noremap;
  566. {
  567.     register char_u    *s;
  568.     register int    newlen;
  569.     register int    addlen;
  570.  
  571.     init_typestr();
  572.  
  573.     /*
  574.      * In typestr there must always be room for MAXMAPLEN + 3 characters
  575.      */
  576.     addlen = STRLEN(str);
  577.     newlen = STRLEN(typestr) + addlen + MAXMAPLEN + 3;
  578.     if (newlen < 0)                /* string is getting too long */
  579.     {
  580.         emsg(e_toocompl);        /* also calls flush_buffers */
  581.         setcursor();
  582.         return FAIL;
  583.     }
  584.     s = alloc(newlen);
  585.     if (s == NULL)                /* out of memory */
  586.         return FAIL;
  587.  
  588.     STRCPY(s, str);
  589.     STRCAT(s, typestr);
  590.     if (typestr != typebuf)
  591.         free(typestr);
  592.     typestr = s;
  593.     typemaplen += addlen;        /* the inserted string is not typed */
  594.     if (no_abbr_cnt)            /* and not used for abbreviations */
  595.         no_abbr_cnt += addlen;
  596.     if (noremap)
  597.     {
  598.         if (noremaplist.nr_off == 0)
  599.             noremaplist.nr_len += addlen;
  600.         else
  601.         {
  602.             struct noremap *p;
  603.  
  604.             p = (struct noremap *)alloc((int)sizeof(struct noremap));
  605.             if (p != NULL)
  606.             {
  607.                 p->nr_next = noremaplist.nr_next;
  608.                 p->nr_off = noremaplist.nr_off;
  609.                 p->nr_len = noremaplist.nr_len;
  610.                 noremaplist.nr_next = p;
  611.                 noremaplist.nr_len = addlen;
  612.                 noremaplist.nr_off = 0;
  613.             }
  614.         }
  615.     }
  616.     else if (noremaplist.nr_len)
  617.         noremaplist.nr_off += addlen;
  618.     return OK;
  619. }
  620.  
  621. /*
  622.  * remove "len" characters from the front of typestr
  623.  */
  624.     void
  625. del_typestr(len)
  626.     int    len;
  627. {
  628.     struct noremap *p;
  629.  
  630.     STRCPY(typestr, typestr + len);
  631.                                         /* remove chars from the buffer */
  632.     if ((typemaplen -= len) < 0)        /* adjust typemaplen */
  633.         typemaplen = 0;
  634.     if ((no_abbr_cnt -= len) < 0)        /* adjust no_abbr_cnt */
  635.         no_abbr_cnt = 0;
  636.  
  637.     while (len)                            /* adjust noremaplist */
  638.     {
  639.         if (noremaplist.nr_off >= len)
  640.         {
  641.             noremaplist.nr_off -= len;
  642.             break;
  643.         }
  644.         len -= noremaplist.nr_off;
  645.         noremaplist.nr_off = 0;
  646.         if (noremaplist.nr_len > len)
  647.         {
  648.             noremaplist.nr_len -= len;
  649.             break;
  650.         }
  651.         len -= noremaplist.nr_len;
  652.         p = noremaplist.nr_next;
  653.         if (p == NULL)
  654.         {
  655.             noremaplist.nr_len = 0;
  656.             break;
  657.         }
  658.         noremaplist.nr_next = p->nr_next;
  659.         noremaplist.nr_len = p->nr_len;
  660.         noremaplist.nr_off = p->nr_off;
  661.         free(p);
  662.     }
  663. }
  664.  
  665. extern int arrow_used;            /* this is in edit.c */
  666.  
  667. /*
  668.  * Write typed characters to script file.
  669.  * If recording is on put the character in the recordbuffer.
  670.  */
  671.     static void
  672. gotchars(s, len)
  673.     char_u    *s;
  674.     int        len;
  675. {
  676.     while (len--)
  677.     {
  678.         updatescript(*s & 255);
  679.  
  680.         if (Recording)
  681.             add_char_buff(&recordbuff, (*s & 255));
  682.         ++s;
  683.     }
  684.  
  685.             /* do not sync in insert mode, unless cursor key has been used */
  686.     if (!(State & (INSERT + CMDLINE)) || arrow_used)        
  687.         u_sync();
  688. }
  689.  
  690. /*
  691.  * open new script file for ":so!" command
  692.  * return OK on success, FAIL on error
  693.  */
  694.     int
  695. openscript(name)
  696.     char_u *name;
  697. {
  698.     int oldcurscript;
  699.  
  700.     if (curscript + 1 == NSCRIPT)
  701.     {
  702.         emsg(e_nesting);
  703.         return FAIL;
  704.     }
  705.     else
  706.     {
  707.         if (scriptin[curscript] != NULL)    /* already reading script */
  708.             ++curscript;
  709.         if ((scriptin[curscript] = fopen((char *)name, READBIN)) == NULL)
  710.         {
  711.             emsg2(e_notopen, name);
  712.             if (curscript)
  713.                 --curscript;
  714.             return FAIL;
  715.         }
  716.         /*
  717.          * With command ":g/pat/so! file" we have to execute the
  718.          * commands from the file now.
  719.          */
  720.         if (global_busy)
  721.         {
  722.             State = NORMAL;
  723.             oldcurscript = curscript;
  724.             do
  725.             {
  726.                 normal();
  727.                 vpeekc();            /* check for end of file */
  728.             }
  729.             while (scriptin[oldcurscript]);
  730.             State = CMDLINE;
  731.         }
  732.     }
  733.     return OK;
  734. }
  735.  
  736. /*
  737.  * updatescipt() is called when a character can be written into the script file
  738.  * or when we have waited some time for a character (c == 0)
  739.  *
  740.  * All the changed memfiles are synced if c == 0 or when the number of typed
  741.  * characters reaches 'updatecount'.
  742.  */
  743.     void
  744. updatescript(c)
  745.     int c;
  746. {
  747.     static int        count = 0;
  748.  
  749.     if (c && scriptout)
  750.         putc(c, scriptout);
  751.     if (c == 0 || ++count >= p_uc)
  752.     {
  753.         ml_sync_all(c == 0);
  754.         count = 0;
  755.     }
  756. }
  757.  
  758. #define NEEDMORET 9999        /* value for incomplete mapping or key-code */
  759.  
  760. /*
  761.  * get a character: 1. from the stuffbuffer
  762.  *                    2. from the typeahead buffer
  763.  *                    3. from the user
  764.  *
  765.  * KeyTyped is set to TRUE in the case the user typed the key.
  766.  * vgetc() (advance is TRUE): really get the character.
  767.  * vpeekc() (advance is FALSE): just look whether there is a character available.
  768.  */
  769.     int
  770. vgetc()
  771. {
  772.     return (vgetorpeek(TRUE));
  773. }
  774.  
  775.     int
  776. vpeekc()
  777. {
  778.     return (vgetorpeek(FALSE));
  779. }
  780.  
  781.     static int
  782. vgetorpeek(advance)
  783.     int        advance;
  784. {
  785.     register int    c;
  786.     int                n = 0;        /* init for GCC */
  787.     int                len;
  788. #ifdef AMIGA
  789.     char_u            *s;
  790. #endif
  791.     register struct mapblock *mp;
  792.     int                timedout = FALSE;        /* waited for more than 1 second
  793.                                                 for mapping to complete */
  794.     int                mapdepth = 0;            /* check for recursive mapping */
  795.     int                mode_deleted = FALSE;    /* set when mode has been deleted */
  796.  
  797.     init_typestr();
  798.     start_stuff();
  799.     if (typemaplen == 0)
  800.         Exec_reg = FALSE;
  801.     do
  802.     {
  803.         c = read_stuff(advance);
  804.         if (c != NUL && !got_int)
  805.             KeyTyped = FALSE;
  806.         else
  807.         {
  808.             /*
  809.              * Loop until we either find a matching mapped key, or we
  810.              * are sure that it is not a mapped key.
  811.              * If a mapped key sequence is found we go back to the start to
  812.              * try re-mapping.
  813.              */
  814.  
  815.             for (;;)
  816.             {
  817.                 len = STRLEN(typestr);
  818.                 breakcheck();                /* check for CTRL-C */
  819.                 if (got_int)
  820.                 {
  821.                     c = inchar(typestr, MAXMAPLEN, 0);    /* flush all input */
  822.                     /*
  823.                      * If inchar returns TRUE (script file was active) or we are
  824.                      * inside a mapping, get out of insert mode.
  825.                      * Otherwise we behave like having gotten a CTRL-C.
  826.                      * As a result typing CTRL-C in insert mode will
  827.                      * really insert a CTRL-C.
  828.                      */
  829.                     if ((c || typemaplen) && (State & (INSERT + CMDLINE)))
  830.                         c = ESC;
  831.                     else
  832.                         c = Ctrl('C');
  833.                     flush_buffers(TRUE);        /* flush all typeahead */
  834.                     break;
  835.                 }
  836.                 else if (len > 0)    /* see if we have a mapped key sequence */
  837.                 {
  838.                     /*
  839.                      * walk through the maplist until we find an
  840.                      * entry that matches.
  841.                      *
  842.                      * Don't look for mappings if:
  843.                      * - timed out
  844.                      * - typestr[0] should not be remapped
  845.                      * - in insert or cmdline mode and 'paste' option set
  846.                      * - waiting for "hit return to continue" and CR or SPACE typed
  847.                      */
  848.                     mp = NULL;
  849.                     if (!timedout && (typemaplen == 0 || (p_remap &&
  850.                             (noremaplist.nr_len == 0 || noremaplist.nr_off != 0)))
  851.                             && !((State & (INSERT + CMDLINE)) && p_paste)
  852.                             && !(State == HITRETURN && (typestr[0] == CR || typestr[0] == ' ')))
  853.                     {
  854.                         for (mp = maplist.m_next; mp; mp = mp->m_next)
  855.                         {
  856.                             if ((mp->m_mode & ABBREV) || !(mp->m_mode & State))
  857.                                 continue;
  858.                             n = mp->m_keylen;
  859.                             if (noremaplist.nr_off != 0 && n > noremaplist.nr_off)
  860.                                 continue;
  861.                             if (!STRNCMP(mp->m_keys, typestr, (size_t)(n > len ? len : n)))
  862.                                 break;
  863.                         }
  864.                     }
  865.                     if (mp == NULL)                    /* no match found */
  866.                     {
  867.                             /*
  868.                              * check if we have a terminal code, when
  869.                              *    mapping is allowed,
  870.                              *  keys have not been mapped,
  871.                              *    and not an ESC sequence, not in insert mode or
  872.                              *        p_ek is on,
  873.                              *    and when not timed out,
  874.                              */
  875.                         if (State != NOMAPPING &&
  876.                                 /* typemaplen == 0 && */ /* allow mapped keys anyway */
  877.                                 (typestr[0] != ESC || p_ek || !(State & INSERT)) &&
  878.                                 !timedout)
  879.                             n = check_termcode(typestr);
  880.                         else
  881.                             n = 0;
  882.                         if (n == 0)        /* no matching terminal code */
  883.                         {
  884. #ifdef AMIGA                    /* check for window bounds report */
  885.                             if (typemaplen == 0 && (typestr[0] & 0xff) == CSI)
  886.                             {
  887.                                 for (s = typestr + 1; isdigit(*s) || *s == ';' || *s == ' '; ++s)
  888.                                     ;
  889.                                 if (*s == 'r' || *s == '|')    /* found one */
  890.                                 {
  891.                                     STRCPY(typestr, s + 1);
  892.                                     set_winsize(0, 0, FALSE);        /* get size and redraw screen */
  893.                                     continue;
  894.                                 }
  895.                                 if (*s == NUL)        /* need more characters */
  896.                                     n = -1;
  897.                             }
  898.                             if (n != -1)            /* got a single character */
  899. #endif
  900.                             {
  901.                                 c = typestr[0] & 255;
  902.                                 if (typemaplen)
  903.                                     KeyTyped = FALSE;
  904.                                 else
  905.                                 {
  906.                                     KeyTyped = TRUE;
  907.                                     if (advance)    /* write char to script file(s) */
  908.                                         gotchars(typestr, 1);
  909.                                 }
  910.                                 if (advance)        /* remove chars from typestr */
  911.                                     del_typestr(1);
  912.                                 break;        /* got character, break for loop */
  913.                             }
  914.                         }
  915.                         if (n > 0)        /* full matching terminal code */
  916.                             continue;    /* try mapping again */
  917.  
  918.                         /* partial match: get some more characters */
  919.                         n = NEEDMORET;
  920.                     }
  921.                     if (n <= len)        /* complete match */
  922.                     {
  923.                         if (n > typemaplen)        /* write chars to script file(s) */
  924.                             gotchars(typestr + typemaplen, n - typemaplen);
  925.  
  926.                         del_typestr(n);    /* remove the mapped keys */
  927.  
  928.                         /*
  929.                          * Put the replacement string in front of mapstr.
  930.                          * The depth check catches ":map x y" and ":map y x".
  931.                          */
  932.                         if (++mapdepth == 1000)
  933.                         {
  934.                             EMSG("recursive mapping");
  935.                             if (State == CMDLINE)
  936.                                 redrawcmdline();
  937.                             else
  938.                                 setcursor();
  939.                             flush_buffers(FALSE);
  940.                             mapdepth = 0;        /* for next one */
  941.                             c = -1;
  942.                             break;
  943.                         }
  944.                         if (ins_typestr(mp->m_str, mp->m_noremap) == FAIL)
  945.                         {
  946.                             c = -1;
  947.                             break;
  948.                         }
  949.                         continue;
  950.                     }
  951.                 }
  952.                 /*
  953.                  * special case: if we get an <ESC> in insert mode and there are
  954.                  * no more characters at once, we pretend to go out of insert mode.
  955.                  * This prevents the one second delay after typing an <ESC>.
  956.                  * If we get something after all, we may have to redisplay the
  957.                  * mode. That the cursor is in the wrong place does not matter.
  958.                  */
  959.                 c = 0;
  960.                 if (advance && len == 1 && typestr[0] == ESC && typemaplen == 0 && (State & INSERT) && (p_timeout || (n == NEEDMORET && p_ttimeout)) && (c = inchar(typestr + len, 2, 0)) == 0)
  961.                 {
  962.                     if (p_smd)
  963.                     {
  964.                         delmode();
  965.                         mode_deleted = TRUE;
  966.                     }
  967.                     if (curwin->w_cursor.col != 0)    /* move cursor one left if possible */
  968.                     {
  969.                         if (curwin->w_col)
  970.                         {
  971.                             if (did_ai)
  972.                             {
  973.                                 if (curwin->w_p_nu)
  974.                                     curwin->w_col = 8;
  975.                                 else
  976.                                     curwin->w_col = 0;
  977.                             }
  978.                             else
  979.                                 --curwin->w_col;
  980.                         }
  981.                         else if (curwin->w_p_wrap && curwin->w_row)
  982.                         {
  983.                                 --curwin->w_row;
  984.                                 curwin->w_col = Columns - 1;
  985.                         }
  986.                     }
  987.                     setcursor();
  988.                     flushbuf();
  989.                 }
  990.                 len += c;
  991.  
  992.                 if (len >= typemaplen + MAXMAPLEN)    /* buffer full, don't map */
  993.                 {
  994.                     timedout = TRUE;
  995.                     continue;
  996.                 }
  997.                 c = inchar(typestr + len, typemaplen + MAXMAPLEN - len, !advance ? 0 : ((len == 0 || !(p_timeout || (p_ttimeout && n == NEEDMORET))) ? -1 : (int)p_tm));
  998.                 if (c <= NUL)        /* no character available */
  999.                 {
  1000.                     if (!advance)
  1001.                         break;
  1002.                     if (len)                /* timed out */
  1003.                     {
  1004.                         timedout = TRUE;
  1005.                         continue;
  1006.                     }
  1007.                 }
  1008.             }        /* for (;;) */
  1009.         }        /* if (!character from stuffbuf) */
  1010.  
  1011.                         /* if advance is FALSE don't loop on NULs */
  1012.     } while (c < 0 || (advance && c == NUL));
  1013.  
  1014.     /*
  1015.      * The "INSERT" message is taken care of here:
  1016.      *   if we return an ESC the message is deleted
  1017.      *   if we don't return an ESC but deleted the message before, redisplay it
  1018.      */
  1019.     if (p_smd && (State & INSERT))
  1020.     {
  1021.         if (c == ESC && !mode_deleted)
  1022.             delmode();
  1023.         else if (c != ESC && mode_deleted)
  1024.             showmode();
  1025.     }
  1026.  
  1027.     return c;
  1028. }
  1029.  
  1030. /*
  1031.  * map[!]                    : show all key mappings
  1032.  * map[!] {lhs}                : show key mapping for {lhs}
  1033.  * map[!] {lhs} {rhs}        : set key mapping for {lhs} to {rhs}
  1034.  * noremap[!] {lhs} {rhs}    : same, but no remapping for {rhs}
  1035.  * unmap[!] {lhs}            : remove key mapping for {lhs}
  1036.  * abbr                        : show all abbreviations
  1037.  * abbr {lhs}                : show abbreviations for {lhs}
  1038.  * abbr {lhs} {rhs}            : set abbreviation for {lhs} to {rhs}
  1039.  * noreabbr {lhs} {rhs}        : same, but no remapping for {rhs}
  1040.  * unabbr {lhs}                : remove abbreviation for {lhs}
  1041.  *
  1042.  * maptype == 1 for unmap command, 2 for noremap command.
  1043.  *
  1044.  * keys is pointer to any arguments.
  1045.  *
  1046.  * for :map      mode is NORMAL 
  1047.  * for :map!  mode is INSERT + CMDLINE
  1048.  * for :cmap  mode is CMDLINE
  1049.  * for :imap  mode is INSERT 
  1050.  * for :abbr  mode is INSERT + CMDLINE + ABBREV
  1051.  * for :iabbr mode is INSERT + ABBREV
  1052.  * for :cabbr mode is CMDLINE + ABBREV
  1053.  * 
  1054.  * Return 0 for success
  1055.  *          1 for invalid arguments
  1056.  *          2 for no match
  1057.  *          3 for ambiguety
  1058.  *          4 for out of mem
  1059.  */
  1060.     int
  1061. domap(maptype, keys, mode)
  1062.     int        maptype;
  1063.     char_u    *keys;
  1064.     int        mode;
  1065. {
  1066.     struct mapblock        *mp, *mprev;
  1067.     char_u                *arg;
  1068.     char_u                *p;
  1069.     int                    n = 0;            /* init for GCC */
  1070.     int                    len = 0;        /* init for GCC */
  1071.     char_u                *newstr;
  1072.     int                    hasarg;
  1073.     int                    haskey;
  1074.     int                    did_it = FALSE;
  1075.     int                    abbrev = 0;
  1076.     int                    round;
  1077.  
  1078.     if (mode & ABBREV)        /* not a mapping but an abbreviation */
  1079.     {
  1080.         abbrev = ABBREV;
  1081.         mode &= ~ABBREV;
  1082.     }
  1083. /*
  1084.  * find end of keys and remove CTRL-Vs in it
  1085.  * with :unmap white space is included in the keys, no argument possible
  1086.  */
  1087.     p = keys;
  1088.     while (*p && (maptype == 1 || !iswhite(*p)))
  1089.     {
  1090.         if (*p == Ctrl('V') && p[1] != NUL)
  1091.             STRCPY(p, p + 1);            /* remove CTRL-V */
  1092.         ++p;
  1093.     }
  1094.     if (*p != NUL)
  1095.         *p++ = NUL;
  1096.     skipspace(&p);
  1097.     arg = p;
  1098.     hasarg = (*arg != NUL);
  1099.     haskey = (*keys != NUL);
  1100.  
  1101.         /* check for :unmap without argument */
  1102.     if (maptype == 1 && !haskey)    
  1103.         return 1;
  1104.  
  1105. /*
  1106.  * remove CTRL-Vs from argument
  1107.  */
  1108.     while (*p)
  1109.     {
  1110.         if (*p == Ctrl('V') && p[1] != NUL)
  1111.             STRCPY(p, p + 1);            /* remove CTRL-V */
  1112.         ++p;
  1113.     }
  1114.  
  1115. /*
  1116.  * check arguments and translate function keys
  1117.  */
  1118.     if (haskey)
  1119.     {
  1120.         if (*keys == '#' && isdigit(*(keys + 1)))    /* function key */
  1121.         {
  1122.             if (*++keys == '0')
  1123.                 *keys = K_F10;
  1124.             else
  1125.                 *keys += K_F1 - '1';
  1126.         }
  1127.         len = STRLEN(keys);
  1128.         if (len > MAXMAPLEN)            /* maximum lenght of MAXMAPLEN chars */
  1129.             return 1;
  1130.  
  1131.         /*
  1132.          * abbreviation must end in id-char
  1133.          * rest must be all id-char or all non-id-char
  1134.          */
  1135.         if (abbrev)
  1136.         {
  1137.             if (!isidchar(*(keys + len - 1)))        /* does not end in id char */
  1138.                 return 1;
  1139.             for (n = 0; n < len - 2; ++n)
  1140.                 if (isidchar(*(keys + n)) != isidchar(*(keys + len - 2)))
  1141.                     return 1;
  1142.         }
  1143.     }
  1144.  
  1145.     if (haskey && hasarg && abbrev)        /* if we will add an abbreviation */
  1146.         no_abbr = FALSE;                /* reset flag that indicates there are
  1147.                                                             no abbreviations */
  1148.  
  1149.     if (!haskey || (maptype != 1 && !hasarg))
  1150.         msg_start();
  1151. /*
  1152.  * Find an entry in the maplist that matches.
  1153.  * For :unmap we may loop two times: once to try to unmap an entry with a
  1154.  * matching 'from' part, a second time, if the first fails, to unmap an
  1155.  * entry with a matching 'to' part. This was done to allow ":ab foo bar" to be
  1156.  * unmapped by typing ":unab foo", where "foo" will be replaced by "bar" because
  1157.  * of the abbreviation.
  1158.  */
  1159.     for (round = 0; (round == 0 || maptype == 1) && round <= 1 && !did_it && !got_int; ++round)
  1160.     {
  1161.         for (mp = maplist.m_next, mprev = &maplist; mp && !got_int; mprev = mp, mp = mp->m_next)
  1162.         {
  1163.                                         /* skip entries with wrong mode */
  1164.             if (!(mp->m_mode & mode) || (mp->m_mode & ABBREV) != abbrev)
  1165.                 continue;
  1166.             if (!haskey)                        /* show all entries */
  1167.             {
  1168.                 showmap(mp);
  1169.                 did_it = TRUE;
  1170.             }
  1171.             else                                /* do we have a match? */
  1172.             {
  1173.                 if (round)        /* second round: try 'to' string for unmap */
  1174.                 {
  1175.                     n = STRLEN(mp->m_str);
  1176.                     p = mp->m_str;
  1177.                 }
  1178.                 else
  1179.                 {
  1180.                     n = mp->m_keylen;
  1181.                     p = mp->m_keys;
  1182.                 }
  1183.                 if (!STRNCMP(p, keys, (size_t)(n < len ? n : len)))
  1184.                 {
  1185.                     if (maptype == 1)            /* delete entry */
  1186.                     {
  1187.                         if (n != len)            /* not a full match */
  1188.                             continue;
  1189.                         /*
  1190.                          * We reset the indicated mode bits. If nothing is left the
  1191.                          * entry is deleted below.
  1192.                          */
  1193.                         mp->m_mode &= (~mode | ABBREV);
  1194.                         did_it = TRUE;            /* remember that we did something */
  1195.                     }
  1196.                     else if (!hasarg)            /* show matching entry */
  1197.                     {
  1198.                         showmap(mp);
  1199.                         did_it = TRUE;
  1200.                     }
  1201.                     else if (n != len)            /* new entry is ambigious */
  1202.                     {
  1203.                         if (abbrev)                /* for abbreviations that's ok */
  1204.                             continue;
  1205.                         return 3;
  1206.                     }
  1207.                     else
  1208.                     {
  1209.                         mp->m_mode &= (~mode | ABBREV);        /* remove mode bits */
  1210.                         if (!(mp->m_mode & ~ABBREV) && !did_it)    /* reuse existing entry */
  1211.                         {
  1212.                             newstr = strsave(arg);
  1213.                             if (newstr == NULL)
  1214.                                 return 4;            /* no mem */
  1215.                             free(mp->m_str);
  1216.                             mp->m_str = newstr;
  1217.                             mp->m_noremap = maptype;
  1218.                             mp->m_mode = mode + abbrev;
  1219.                             did_it = TRUE;
  1220.                         }
  1221.                     }
  1222.                     if (!(mp->m_mode & ~ABBREV))        /* entry can be deleted */
  1223.                     {
  1224.                         free(mp->m_keys);
  1225.                         free(mp->m_str);
  1226.                         mprev->m_next = mp->m_next;
  1227.                         free(mp);
  1228.                         mp = mprev;                    /* continue with next entry */
  1229.                     }
  1230.                 }
  1231.             }
  1232.         }
  1233.     }
  1234.  
  1235.     if (maptype == 1)                        /* delete entry */
  1236.     {
  1237.         if (did_it)
  1238.             return 0;                        /* removed OK */
  1239.         else
  1240.             return 2;                        /* no match */
  1241.     }
  1242.  
  1243.     if (!haskey || !hasarg)                    /* print entries */
  1244.     {
  1245.         if (did_it)
  1246.             msg_end();
  1247.         else if (abbrev)
  1248.             MSG("No abbreviation found");
  1249.         else
  1250.             MSG("No mapping found");
  1251.         return 0;                            /* listing finished */
  1252.     }
  1253.  
  1254.     if (did_it)                    /* have added the new entry already */
  1255.         return 0;
  1256. /*
  1257.  * get here when we have to add a new entry
  1258.  */
  1259.         /* allocate a new entry for the maplist */
  1260.     mp = (struct mapblock *)alloc((unsigned)sizeof(struct mapblock));
  1261.     if (mp == NULL)
  1262.         return 4;            /* no mem */
  1263.     mp->m_keys = strsave(keys);
  1264.     mp->m_str = strsave(arg);
  1265.     if (mp->m_keys == NULL || mp->m_str == NULL)
  1266.     {
  1267.         free(mp->m_keys);
  1268.         free(mp->m_str);
  1269.         free(mp);
  1270.         return 4;        /* no mem */
  1271.     }
  1272.     mp->m_keylen = STRLEN(mp->m_keys);
  1273.     mp->m_noremap = maptype;
  1274.     mp->m_mode = mode + abbrev;
  1275.  
  1276.     /* add the new entry in front of the maplist */
  1277.     mp->m_next = maplist.m_next;
  1278.     maplist.m_next = mp;
  1279.  
  1280.     return 0;                /* added OK */
  1281. }
  1282.  
  1283.     static void
  1284. showmap(mp)
  1285.     struct mapblock *mp;
  1286. {
  1287.     int len;
  1288.  
  1289.     msg_pos(-1, 0);                        /* always start in column 0 */
  1290.     if ((mp->m_mode & (INSERT + CMDLINE)) == INSERT + CMDLINE)
  1291.         msg_outstr((char_u *)"! ");
  1292.     else if (mp->m_mode & INSERT)
  1293.         msg_outstr((char_u *)"i ");
  1294.     else if (mp->m_mode & CMDLINE)
  1295.         msg_outstr((char_u *)"c ");
  1296.     len = msg_outtrans(mp->m_keys, -1);    /* get length of what we have written */
  1297.     do
  1298.     {
  1299.         msg_outchar(' ');                /* padd with blanks */
  1300.         ++len;
  1301.     } while (len < 12);
  1302.     if (mp->m_noremap)
  1303.         msg_outchar('*');
  1304.     else
  1305.         msg_outchar(' ');
  1306.     msg_outtrans(mp->m_str, -1);
  1307.     msg_outchar('\n');
  1308.     flushbuf();                            /* show one line at a time */
  1309. }
  1310.  
  1311. /*
  1312.  * Check for an abbreviation.
  1313.  * Cursor is at ptr[col]. When inserting, mincol is where insert started.
  1314.  * "c" is the character typed before check_abbr was called.
  1315.  *
  1316.  * Historic vi practice: The last character of an abbreviation must be an id
  1317.  * character ([a-zA-Z0-9_]). The characters in front of it must be all id
  1318.  * characters or all non-id characters. This allows for abbr. "#i" to "#include".
  1319.  *
  1320.  * return TRUE if there is an abbreviation, FALSE if not
  1321.  */
  1322.     int
  1323. check_abbr(c, ptr, col, mincol)
  1324.     int        c;
  1325.     char_u    *ptr;
  1326.     int        col;
  1327.     int        mincol;
  1328. {
  1329.     int                len;
  1330.     int                j;
  1331.     char_u            tb[3];
  1332.     struct mapblock *mp;
  1333.     int                is_id = TRUE;
  1334.  
  1335.     if (no_abbr_cnt)        /* abbrev. are not recursive */
  1336.         return FALSE;
  1337.  
  1338.     if (col == 0 || !isidchar(ptr[col - 1]))    /* cannot be an abbr. */
  1339.         return FALSE;
  1340.  
  1341.     if (col > 1)
  1342.         is_id = isidchar(ptr[col - 2]);
  1343.     for (len = col - 1; len > 0 && !isspace(ptr[len - 1]) &&
  1344.                                 is_id == isidchar(ptr[len - 1]); --len)
  1345.         ;
  1346.  
  1347.     if (len < mincol)
  1348.         len = mincol;
  1349.     if (len < col)                /* there is a word in front of the cursor */
  1350.     {
  1351.         ptr += len;
  1352.         len = col - len;
  1353.         for (mp = maplist.m_next; mp; mp = mp->m_next)
  1354.         {
  1355.                     /* find entries with right mode and keys */
  1356.             if ((mp->m_mode & ABBREV) == ABBREV &&
  1357.                         (mp->m_mode & State) &&
  1358.                         mp->m_keylen == len &&
  1359.                         !STRNCMP(mp->m_keys, ptr, (size_t)len))
  1360.                 break;
  1361.         }
  1362.         if (mp)                                /* found a match */
  1363.         {
  1364.             j = 0;
  1365.             if (c < 0x100 && (c < ' ' || c > '~'))
  1366.                 tb[j++] = Ctrl('V');        /* special char needs CTRL-V */
  1367.             tb[j++] = c;
  1368.             tb[j] = NUL;
  1369.             (void)ins_typestr(tb, TRUE);                    /* insert the last typed char */
  1370.             (void)ins_typestr(mp->m_str, mp->m_noremap);    /* insert the to string */
  1371.             no_abbr_cnt += STRLEN(mp->m_str) + j;    /* no abbrev. for these chars */
  1372.             while (len--)
  1373.                 (void)ins_typestr((char_u *)"\b", TRUE);    /* delete the from string */
  1374.             return TRUE;
  1375.         }
  1376.     }
  1377.     return FALSE;
  1378. }
  1379.  
  1380. /*
  1381.  * Write map commands for the current mappings to an .exrc file.
  1382.  * Return FAIL on error, OK otherwise.
  1383.  */
  1384.     int
  1385. makemap(fd)
  1386.     FILE *fd;
  1387. {
  1388.     struct mapblock *mp;
  1389.     char_u            c1;
  1390.     char_u             *p;
  1391.  
  1392.     for (mp = maplist.m_next; mp; mp = mp->m_next)
  1393.     {
  1394.         c1 = NUL;
  1395.         p = (char_u *)"map";
  1396.         switch (mp->m_mode)
  1397.         {
  1398.         case NORMAL:
  1399.             break;
  1400.         case CMDLINE + INSERT:
  1401.             p = (char_u *)"map!";
  1402.             break;
  1403.         case CMDLINE:
  1404.             c1 = 'c';
  1405.             break;
  1406.         case INSERT:
  1407.             c1 = 'i';
  1408.             break;
  1409.         case INSERT + CMDLINE + ABBREV:
  1410.             p = (char_u *)"abbr";
  1411.             break;
  1412.         case CMDLINE + ABBREV:
  1413.             c1 = 'c';
  1414.             p = (char_u *)"abbr";
  1415.             break;
  1416.         case INSERT + ABBREV:
  1417.             c1 = 'i';
  1418.             p = (char_u *)"abbr";
  1419.             break;
  1420.         default:
  1421.             EMSG("makemap: Illegal mode");
  1422.             return FAIL;
  1423.         }
  1424.         if (c1 && putc(c1, fd) < 0)
  1425.             return FAIL;
  1426.         if (mp->m_noremap && fprintf(fd, "nore") < 0)
  1427.             return FAIL;
  1428.         if (fprintf(fd, (char *)p) < 0)
  1429.             return FAIL;
  1430.  
  1431.         if (    putc(' ', fd) < 0 || putescstr(fd, mp->m_keys, FALSE) == FAIL ||
  1432.                 putc(' ', fd) < 0 || putescstr(fd, mp->m_str, FALSE) == FAIL ||
  1433. #ifdef MSDOS
  1434.                 putc('\r', fd) < 0 ||
  1435. #endif
  1436.                 putc('\n', fd) < 0)
  1437.             return FAIL;
  1438.     }
  1439.     return OK;
  1440. }
  1441.  
  1442. /*
  1443.  * write escape string to file
  1444.  *
  1445.  * return FAIL for failure, OK otherwise
  1446.  */
  1447.     int
  1448. putescstr(fd, str, set)
  1449.     FILE        *fd;
  1450.     char_u        *str;
  1451.     int            set;        /* TRUE for makeset, FALSE for makemap */
  1452. {
  1453.     for ( ; *str; ++str)
  1454.     {
  1455.         /*
  1456.          * some characters have to be escaped with CTRL-V to
  1457.          * prevent them from misinterpreted in DoOneCmd().
  1458.          * A space has to be escaped with a backslash to
  1459.          * prevent it to be misinterpreted in doset().
  1460.          */
  1461.         if (*str < ' ' || *str > '~' || (*str == ' ' && !set))
  1462.         {
  1463.             if (putc(Ctrl('V'), fd) < 0)
  1464.                 return FAIL;
  1465.         }
  1466.         else if ((set && *str == ' ') || *str == '|')
  1467.         {
  1468.             if (putc('\\', fd) < 0)
  1469.                 return FAIL;
  1470.         }
  1471.         if (putc(*str, fd) < 0)
  1472.             return FAIL;
  1473.     }
  1474.     return OK;
  1475. }
  1476.